package csbuf

import (
	"encoding/binary"
	"encoding/base64"
	"fmt"
	"errors"
	"sync"
)

var _ = fmt.Println

type CsbufMessage interface {
	GetProtoId() uint16

	// 序列化，把结构体转换成字符串
	Marshal(* CsbufParser)

	// 反序列化，把字符串转换到结构体上
	Unmarshal(* CsbufParser)
}

func GetProtoIdBuf(msg CsbufMessage) []byte {
	b := make([]byte, 2)
	binary.BigEndian.PutUint16(b, msg.GetProtoId())
	return b
}

func GetMarshalData(msg CsbufMessage, parser *CsbufParser) []byte {
	parser.Clear()
	msg.Marshal(parser)
	return parser.GetData()
}

func GetMessageData(msg CsbufMessage, parser *CsbufParser) []byte {
	parser.Clear()
	msg.Marshal(parser)
	data := parser.GetData()
	b := make([]byte, 2 + len(data))
	binary.BigEndian.PutUint16(b, msg.GetProtoId())
	copy(b[2:], data)
	return b
}

func GetMessageData2(msg CsbufMessage) []byte {
	return GetMessageData(msg, &CsbufParser{})
}

func GetMessage(buf []byte, parser *CsbufParser, groupNumber int) CsbufMessage {
	if len(buf) < 2 {
		fmt.Println("csbuf GetMessage buf len err: ", buf)
		return nil
	}
	protoId := binary.BigEndian.Uint16(buf)
	msg := CreateMessage(protoId, groupNumber)
	if msg != nil && len(buf) >= 2 {
		parser.Clear()
		parser.SetData(buf[2:])
		msg.Unmarshal(parser)
	} else {
		fmt.Println("csbuf GetMessage err: ", protoId, buf)
	}

	return msg
}

func GetProtoId(buf []byte) uint16 {
	if len(buf) >= 2 {
		return binary.BigEndian.Uint16(buf)
	}
	return 0
}

// 把协议转换成 http base64 传递文本
func GetBase64Buf(msg CsbufMessage) []byte {
	src := GetMessageData(msg, new(CsbufParser))
	buf := make([]byte, base64.StdEncoding.EncodedLen(len(src)))
	base64.StdEncoding.Encode(buf, src)
	return buf
}

// 把 http base64 文件转换成协议
func GetBase64Msg(data string)(uint16, CsbufMessage) {
	buf, err := base64.StdEncoding.DecodeString(data)
	if err == nil {
		protoId := binary.BigEndian.Uint16(buf)
		msg := CreateMessage(protoId, 1)
		if msg != nil && len(buf) >= 2 {
			rp := new(CsbufParser)
			rp.SetData(buf[2:])
			msg.Unmarshal(rp)
		}
		return protoId, msg
	}
	return 0, nil
}

var msgs = make(map[uint16]func() CsbufMessage)
var msgList = [10]map[uint16]func() CsbufMessage {
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
	make(map[uint16]func() CsbufMessage),
}

func RegisterCreater(protoId uint16, fn func() CsbufMessage, groupNumber int) {
	msgList[groupNumber][protoId] = fn
}

func CreateMessage(protoId uint16, groupNumber int) CsbufMessage {
	creater, ok := msgList[groupNumber][protoId]
	if ok {
		return creater()
	} else if groupNumber != 0 {
		creater, ok = msgList[0][protoId]
		if ok {
			return creater()
		}
	}

	return nil
}

type CsbufProtoParser struct {
	sync.Mutex
	parser *CsbufParser
	parser2 *CsbufParser
	group int
}

func NewProtoParser(group int) *CsbufProtoParser {
	p := &CsbufProtoParser{sync.Mutex{}, &CsbufParser{}, &CsbufParser{}, group}
	return p
}

func (p *CsbufProtoParser) Unmarshal(data []byte) (uint16, interface{}, error) {
	p.Lock()
	defer p.Unlock()
	msg := GetMessage(data, p.parser, p.group)
	if nil != msg {
		return msg.GetProtoId(), msg, nil
	}
	return 0, nil, errors.New("wrong proto data")
}

func (p *CsbufProtoParser) Marshal(msg interface{}) ([]byte, error) {
	p.Lock()
	defer p.Unlock()
	m, ok := msg.(CsbufMessage)
	if ok {
		return GetMessageData(m, p.parser2), nil
	}
	return nil, errors.New("wrong proto")
}